﻿using LightCAD.Runtime;
using SkiaSharp;
using SkiaSharp.Views.Desktop;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace LightCAD.UI
{
    public partial class ViewportControl : UserControl, IViewportControl
    {
        public static int HoverDelayTime = 500;//悬停延迟时间，毫秒
        private MainUI mainUI;

        internal event Action<string, MouseEventRuntime> mouseEvent;
        internal event Action<KeyEventRuntime> keyDown;
        internal event Action<KeyEventRuntime> keyUp;
        internal event Action<Bounds> sizeChanged;
        internal event Action<SKSurface> skRender;

        internal event EventHandler mouseDown;

        private MouseEventArgs movedEvent;
        private bool isPointerHovering;
        private Point preHoverLocation;
        private System.Timers.Timer hoverTimer;

        public ViewportControl()
        {
            InitializeComponent();

            this.Load += ViewportControl_Load;

            this.contextMenu.ItemClicked += ContextMenu_ItemClicked;

            this.skglControl1.MouseDown += SkglControl_MouseDown;
            this.skglControl1.MouseMove += SkglControl_MouseMove;
            this.skglControl1.MouseUp += SkglControl_MouseUp;
            this.skglControl1.MouseWheel += SkglControl_MouseWheel;
            this.skglControl1.KeyDown += SkglControl_KeyDown;
            this.skglControl1.KeyUp += SkglControl1_KeyUp;

            this.SizeChanged += ViewportControl_SizeChanged; ;
            this.VisibleChanged += ViewportControl_VisibleChanged;
            this.skglControl1.PaintSurface += SkglControl1_PaintSurface;
            this.Paint += ViewportControl_Paint;
            this.tmRender.Enabled = true;

            this.hoverTimer = new System.Timers.Timer();
            this.hoverTimer.Interval = HoverDelayTime;
            this.hoverTimer.Elapsed += HoverTimer_Elapsed;

        }

        private void ViewportControl_Load(object? sender, EventArgs e)
        {
            this.mainUI = UIUtils.GetParent<MainUI>(this);
        }



        private async void ContextMenu_ItemClicked(object? sender, ToolStripItemClickedEventArgs e)
        {
            var reault = await Task<CommandResult>.Run(() =>
            {
                //这里存在非常迷惑的问题，Task虽然是异步，但有可能执行很快，效果上类似同步执行，只有延迟一段时间，才能保证关闭事件里的代码优先执行
                Thread.Sleep(10);
                return CommandCenter.Instance.Execute("");
            });
            //Do something
        }

        private void ViewportControl_Paint(object? sender, PaintEventArgs e)
        {
           
        }

        public bool HoverEnabled { get; set; } = true;
        public long ViewportId => (long)this.Tag;

        public void AttachEvents(
            Action<string, MouseEventRuntime> mouseEvent,
            Action<KeyEventRuntime> keyDown,
            Action<KeyEventRuntime> keyUp,
            Action<Bounds> sizeChanged,
            Action<SKSurface> skRender)
        {
            if(mouseEvent!=null) this.mouseEvent += mouseEvent;
            if (keyDown != null) this.keyDown += keyDown;
            if (keyUp != null) this.keyUp += keyUp;
            if (sizeChanged != null) this.sizeChanged += sizeChanged;
            if (skRender != null) this.skRender += skRender;
        }

        public void DetachEvents(
            Action<string, MouseEventRuntime> mouseEvent,
            Action<KeyEventRuntime> keyDown,
             Action<KeyEventRuntime> keyUp,
           Action<Bounds> sizeChanged,
            Action<SKSurface> skRender)
        {
            if (mouseEvent != null) this.mouseEvent -= mouseEvent;
            if (keyDown != null) this.keyDown -= keyDown;
            if (keyUp != null) this.keyUp -= keyDown;
            if (sizeChanged != null) this.sizeChanged -= sizeChanged;
            if (skRender != null) this.skRender -= skRender;
        }

        internal void ViewportControl_SizeChanged(object? sender, EventArgs e)
        {
            this.sizeChanged?.Invoke(this.Bounds.ToRuntime());
        }

        private void SkglControl_MouseDown(object? sender, MouseEventArgs e)
        {
            mouseDown?.Invoke(this, e);
            mouseEvent?.Invoke("Down", e.ToRuntime());
        }


        private void HoverTimer_Elapsed(object? sender, System.Timers.ElapsedEventArgs e)
        {
            hoverTimer.Stop();
            this.isPointerHovering = true;
            HoverEventStart();
        }
        private void HoverEventStart()
        {
            preHoverLocation = this.movedEvent.Location;
            mouseEvent?.Invoke("HoverStart", this.movedEvent.ToRuntime());
        }
        private void HoverEventEnd()
        {
            mouseEvent?.Invoke("HoverEnd", this.movedEvent.ToRuntime());
        }

        private void SkglControl_MouseMove(object? sender, MouseEventArgs e)
        {
            this.movedEvent = e;

            if (this.mainUI!=null && this.mainUI.IsActive)
            {
                skglControl1.Focus();
            }
            
            //模拟处理悬停事件，停在某点一段时间触发事件，移动光标悬停结束，等待下一次悬停事件
            //需要位置发生改变才停止Hover事件，因为ToolTip的显示，会导致Move事件的重复触发
            if (this.isPointerHovering && preHoverLocation != e.Location)
            {
                this.isPointerHovering = false;
                HoverEventEnd();
            }
            hoverTimer.Stop();
            hoverTimer.Start();

            mouseEvent?.Invoke("Move", e.ToRuntime());
        }
        private void SkglControl_MouseUp(object? sender, MouseEventArgs e)
        {
            mouseEvent?.Invoke("Up", e.ToRuntime());
        }
        private void SkglControl_MouseWheel(object? sender, MouseEventArgs e)
        {
            mouseEvent?.Invoke("Wheel", e.ToRuntime());
        }

        private void SkglControl_KeyDown(object? sender, KeyEventArgs e)
        {
            keyDown?.Invoke(e.ToRuntime());
        }

        private void SkglControl1_KeyUp(object? sender, KeyEventArgs e)
        {
            keyUp?.Invoke(e.ToRuntime());
        }

        private void ViewportControl_VisibleChanged(object? sender, EventArgs e)
        {
            this.tmRender.Enabled = this.Visible && this.isActive;
        }

        public bool ForceRefresh { get; set; }


        private bool inRender;

        private bool isActive;
        public bool IsActive
        {
            get
            {
                return isActive;
            }

            internal set
            {
                isActive = value;
                this.tmRender.Enabled = value && this.isActive;
            }
        }
        public string Guid { get; }= System.Guid.NewGuid().ToString();

        private void SkglControl1_PaintSurface(object? sender, SkiaSharp.Views.Desktop.SKPaintGLSurfaceEventArgs e)
        {
            inRender = true;
            try
            {
                skRender?.Invoke(e.Surface);
            }
            finally
            {
                inRender = false;
            }
        }

        private void timer1_Tick(object sender, EventArgs e)
        {
            if (inRender) return;
            this.skglControl1.Invalidate();
        }


        public void ShowHoverTip(object content)
        {

        }

        private Action closedAction;
        public void PopupContextMenu(Action closedAction)
        {
            this.closedAction = closedAction;
            this.contextMenu.Show(Control.MousePosition.X, Control.MousePosition.Y);
            this.contextMenu.Closed += ContextMenu_Closed;
        }

        private void ContextMenu_Closed(object? sender, ToolStripDropDownClosedEventArgs e)
        {
            this.contextMenu.Closed -= ContextMenu_Closed;
            closedAction?.Invoke();
        }

        public void ShowInfo(string type, object info)
        {
            lblInfo.Text = info?.ToString();
        }

        public IPopupObject GetPopupObject(string type)
        {
            
            if (type == "ElementHover")
            {
                if (!this.components.Components.OfType<IPopupObject>().Any())
                {
                    this.components.Add(PopupInfo.Instance);
                }
                return PopupInfo.Instance;
            }
            return null;
        }

        public SKPoint PointToScreen(SKPoint point)
        {
            return this.PointToScreen(new Point((int)point.X, (int)point.Y)).ToSKPoint();
        }
    }
}
